package sz

import (
	"common/logs"
	"common/ut"
	"core/remote"
	"encoding/json"
	"game/component/base"
	"game/component/proto"
	"github.com/jinzhu/copier"
	"time"
)

type GameFrame struct {
	r          base.RoomFrame
	gameRule   *proto.GameRule
	gameData   *GameData
	logic      *Logic
	gameResult *GameResult
}

func (game *GameFrame) GameMessageHandle(user *proto.RoomUserInfo, session *remote.Session, msg []byte) {
	// 1. 解析参数
	param := new(MessageRequest)
	if err := json.Unmarshal(msg, param); err != nil {
		logs.Error("参数解析异常：%v", err)
		return
	}
	// 2. 根据请求进行对应的处理
	switch param.Type {
	case proto.GameLookNotify: // 看牌请求
		game.onGameLook(user, session, param.Data.Cuopai)
	case proto.GamePourScoreNotify: // 下分请求
		game.onGamePourScore(user, session, param.Data.Score, param.Data.Type)
	case proto.GameCompareNotify: // 比牌请求
		game.onGameCompare(user, session, param.Data.ChairID)
	case proto.GameAbandonNotify: // 弃牌请求
		game.onGameAbandon(user, session)
	}
}

func NewGameFrame(r base.RoomFrame, gameRule *proto.GameRule) *GameFrame {
	return &GameFrame{
		r:        r,
		gameRule: gameRule,
		gameData: initGameData(gameRule),
		logic:    NewLogic(),
	}
}

func initGameData(rule *proto.GameRule) *GameData {
	game := GameData{
		GameType:   GameType(rule.GameFrameType),
		BaseScore:  rule.BaseScore,
		ChairCount: rule.MaxPlayerCount, // 座位数
	}
	game.PourScores = make([][]int, game.ChairCount)
	// 手牌
	game.HandCards = make([][]int, game.ChairCount)
	// 看牌
	game.LookCards = make([]int, game.ChairCount)
	game.CurScores = make([]int, game.ChairCount)
	game.UserStatusArray = make([]UserStatus, game.ChairCount)
	game.UserTrustArray = make([]bool, 10)
	game.Loser = make([]int, 0)
	game.Winner = make([]int, 0)
	return &game
}

func (game *GameFrame) StartGame(session *remote.Session, user *proto.RoomUserInfo) {
	// 1. 用户金币推送
	users := game.getAllUsers()
	pushMsg := map[string]any{
		"gold":       user.UserInfo.Gold,
		"pushRouter": "UpdateUserInfoPush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 2. 庄家推送
	if game.gameData.CurBureau == 0 {
		// 首局，随机一个庄家
		// 庄家每次开始游戏，是首次可以进行操作的
		game.gameData.BankerChairID = ut.Rand(len(users))
	}
	// 将可以进行操作的座次号设置为庄家
	game.gameData.CurChairID = game.gameData.BankerChairID
	pushMsg = map[string]any{
		"type": proto.GameBankerPush,
		"data": map[string]any{
			"bankerChairID": game.gameData.BankerChairID,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 3. 局数推送
	game.gameData.CurBureau++
	pushMsg = map[string]any{
		"type": proto.GameBureauPush,
		"data": map[string]any{
			"curBureau": game.gameData.CurBureau,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 4. 游戏状态：发牌推送
	game.gameData.GameStatus = SendCards
	pushMsg = map[string]any{
		"type": proto.GameStatusPush,
		"data": map[string]any{
			"gameStatus": game.gameData.GameStatus,
			"tick":       0,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 5. 发牌
	game.sendCards(session, users)
	// 6. 下分推送
	game.gameData.GameStatus = PourScore
	pushMsg = map[string]any{
		"type": proto.GameStatusPush,
		"data": map[string]any{
			"gameStatus": game.gameData.GameStatus,
			"tick":       30,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 7. 用户信息推送
	game.gameData.CurScore = game.gameRule.AddScores[0] * game.gameRule.BaseScore
	gameUsers := game.r.GetUsers()
	for _, user := range gameUsers {
		pushMsg = map[string]any{
			"type": proto.GamePourScorePush,
			"data": map[string]any{
				"chairID":    user.ChairId,
				"score":      game.gameData.CurScore,
				"chairScore": 1,
				"scores":     1,
				"type":       0,
			},
			"pushRouter": "GameMessagePush",
		}
		session.Push([]string{user.UserInfo.Uid}, pushMsg, "ServerMessagePush")
	}
	// 8. 轮数推送
	game.gameData.Round = 1
	pushMsg = map[string]any{
		"type": proto.GameRoundPush,
		"data": map[string]any{
			"round": game.gameData.Round,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 9. 操作推送
	// 9.1 curChairID 是当前可以做操作的用户，比如说弃牌、加注
	for _, user := range gameUsers {
		pushMsg = map[string]any{
			"type": proto.GameTurnPush,
			"data": map[string]any{
				"curChairID": game.gameData.CurChairID,
				"curScore":   game.gameData.CurScore,
			},
			"pushRouter": "GameMessagePush",
		}
		session.Push([]string{user.UserInfo.Uid}, pushMsg, "ServerMessagePush")
	}
}

func (game *GameFrame) sendCards(session *remote.Session, users []string) {
	// 1. 先进行洗牌
	game.logic.washCards()
	for i := range game.gameData.ChairCount {
		if game.IsPlayingChairId(i) {
			// 2. 再进行发牌
			game.gameData.HandCards[i] = game.logic.getCards()
		}
	}
	// 3. 如果没有看牌，则是暗牌
	hands := make([][]int, game.gameData.ChairCount)
	for i, hand := range game.gameData.HandCards {
		if hand != nil {
			hands[i] = []int{0, 0, 0}
		}
	}
	pushMsg := map[string]any{
		"type": proto.GameSendCardsPush,
		"data": map[string]any{
			"handCards": hands,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
}

func (game *GameFrame) getAllUsers() []string {
	users := game.r.GetUsers()
	result := make([]string, len(users))
	for _, user := range users {
		result = append(result, user.UserInfo.Uid)
	}
	return result
}

func (game *GameFrame) GetGameData(session *remote.Session) any {
	user := game.r.GetUsers()[session.Msg.Uid]
	gameData := new(GameData)
	// 深拷贝
	copier.CopyWithOption(gameData, game.gameData, copier.Option{DeepCopy: true})
	// 隐藏其它用户的手牌
	for i := range game.gameData.ChairCount {
		if game.gameData.HandCards[i] != nil {
			gameData.HandCards[i] = make([]int, 3)
		} else {
			gameData.HandCards[i] = nil
		}
	}
	if game.gameData.LookCards[user.ChairId] == 1 {
		// 已经看牌了
		gameData.HandCards[user.ChairId] = game.gameData.HandCards[user.ChairId]
	}
	return gameData
}

// IsPlayingChairId 判断当前玩家是否在游戏中
func (game *GameFrame) IsPlayingChairId(chairId int) bool {
	for _, user := range game.r.GetUsers() {
		if user.ChairId == chairId && user.UserStatus == proto.Playing {
			return true
		}
	}
	return false
}

// 看牌
func (game *GameFrame) onGameLook(user *proto.RoomUserInfo, session *remote.Session, cuopai bool) {
	if game.gameData.GameStatus != PourScore || game.gameData.CurChairID != user.ChairId {
		logs.Warn("房间ID:%s 三张游戏看牌错误：gameStatus=%d,curChairID=%d,chairID=%d", game.r.GetId(), game.gameData.GameStatus, game.gameData.CurChairID, user.ChairId)
		return
	} else if !game.IsPlayingChairId(user.ChairId) {
		logs.Warn("房间ID：%s，用户 %s 不在游戏状态", game.r.GetId(), user.UserInfo.Uid)
		return
	}
	// 将当前用户的状态设置为看牌状态
	game.gameData.UserStatusArray[user.ChairId] = Look
	game.gameData.LookCards[user.ChairId] = 1
	// 通知看牌用户手牌，并且通知其它用户该用户已看牌
	for _, user := range game.r.GetUsers() {
		if game.gameData.CurChairID == user.ChairId {
			pushMsg := map[string]any{
				"type": proto.GameLookPush,
				"data": map[string]any{
					"cards":   game.gameData.HandCards[user.ChairId], // 当前用户手牌
					"chairID": game.gameData.CurChairID,              // 看牌用户座次
					"cuopai":  cuopai,
				},
				"pushRouter": "GameMessagePush",
			}
			session.Push([]string{user.UserInfo.Uid}, pushMsg, "ServerMessagePush")
		} else {
			pushMsg := map[string]any{
				"type": proto.GameLookPush,
				"data": map[string]any{
					"cards":   nil,                      // 当前用户手牌
					"chairID": game.gameData.CurChairID, // 看牌用户座次
					"cuopai":  cuopai,
				},
				"pushRouter": "GameMessagePush",
			}
			session.Push([]string{user.UserInfo.Uid}, pushMsg, "ServerMessagePush")
		}
	}
}

// 用户用跟注或加注
func (game *GameFrame) onGamePourScore(user *proto.RoomUserInfo, session *remote.Session, score int, t int) {
	if game.gameData.GameStatus != PourScore || game.gameData.CurChairID != user.ChairId {
		logs.Warn("房间ID:%s 三张游戏看牌错误：gameStatus=%d,curChairID=%d,chairID=%d", game.r.GetId(), game.gameData.GameStatus, game.gameData.CurChairID, user.ChairId)
		return
	} else if !game.IsPlayingChairId(user.ChairId) {
		logs.Warn("房间ID：%s，用户 %s 不在游戏状态", game.r.GetId(), user.UserInfo.Uid)
		return
	} else if score < 0 {
		logs.Warn("房间ID：%s，用户 %s 下分小于 0", game.r.GetId(), user.UserInfo.Uid)
		return
	}
	if game.gameData.PourScores[user.ChairId] == nil {
		game.gameData.PourScores[user.ChairId] = make([]int, 0)
	}
	// 1. 进行下分操作
	game.gameData.PourScores[user.ChairId] = append(game.gameData.PourScores[user.ChairId], score)
	// 计算所有人的分数
	var scores int
	for i := range game.gameData.ChairCount {
		pourScore := game.gameData.PourScores[i]
		if pourScore == nil {
			continue
		}
		for _, sc := range pourScore {
			scores += sc
		}
	}
	allUsers := game.getAllUsers()
	// 2. 当前座次总分
	var chairScore int
	pourScore := game.gameData.PourScores[user.ChairId]
	for _, sc := range pourScore {
		chairScore += sc
	}
	pushMsg := map[string]any{
		"type": proto.GamePourScorePush,
		"data": map[string]any{
			"chairID":    user.ChairId, // 用户座次
			"score":      score,        // 分数
			"chairScore": chairScore,
			"scores":     scores, // 总分数
			"type":       t,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(allUsers, pushMsg, "ServerMessagePush")
	game.endPourScore(allUsers, session)
}

// 获取当前轮次
func (game *GameFrame) getCurRound() int {
	cur := game.gameData.CurChairID
	for i := 0; i < game.gameData.ChairCount; i++ {
		cur++
		cur = cur % game.gameData.ChairCount
		if game.IsPlayingChairId(cur) {
			return len(game.gameData.PourScores[cur])
		}
	}
	return 1
}

// 比牌
func (game *GameFrame) onGameCompare(user *proto.RoomUserInfo, session *remote.Session, toChairID int) {
	allUsers := game.getAllUsers()
	// 1. 比牌
	result := game.logic.CompareCards(game.gameData.HandCards[user.ChairId], game.gameData.HandCards[toChairID])
	// 2. 如果结果为和，那么主动比牌者输
	if result == 0 {
		result = -1
	}
	fromChairID := user.ChairId
	// 3. 处理结果
	var winChairID int
	var loseChairID int
	if result > 0 {
		winChairID = fromChairID
		loseChairID = toChairID
		pushMsg := map[string]any{
			"type": proto.GameComparePush,
			"data": map[string]any{
				"fromChairID": fromChairID,
				"loseChairID": toChairID,
				"toChairID":   toChairID,
				"winChairID":  fromChairID,
			},
			"pushRouter": "GameMessagePush",
		}
		session.Push(allUsers, pushMsg, "ServerMessagePush")
	} else {
		winChairID = toChairID
		loseChairID = fromChairID
		pushMsg := map[string]any{
			"type": proto.GameComparePush,
			"data": map[string]any{
				"fromChairID": fromChairID,
				"loseChairID": fromChairID,
				"toChairID":   toChairID,
				"winChairID":  toChairID,
			},
			"pushRouter": "GameMessagePush",
		}
		session.Push(allUsers, pushMsg, "ServerMessagePush")
	}
	game.gameData.UserStatusArray[winChairID] = Win
	game.gameData.UserStatusArray[loseChairID] = Lose
	game.gameData.Loser = append(game.gameData.Loser, loseChairID)
	game.gameData.Winner = append(game.gameData.Winner, winChairID)
	// 4. 赢了之后还需要继续和其它玩家比牌
	if winChairID == fromChairID {
		// TODO

	}
	game.endPourScore(allUsers, session)
}

func (game *GameFrame) endPourScore(users []string, session *remote.Session) {
	// 1. 推送轮数
	pushMsg := map[string]any{
		"type": proto.GameRoundPush,
		"data": map[string]any{
			"round": game.getCurRound(),
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 2. 判断当前玩家 没有失败且只剩下一个时
	var gameCount int
	for i := range game.gameData.ChairCount {
		if game.IsPlayingChairId(i) && ut.Contains(game.gameData.Loser, i) {
			gameCount++
		}
	}
	if gameCount == 1 {
		// 进行结算
		game.result(session, users)
		return
	}
	// 3. 座次向后移动
	for i := 0; i < game.gameData.ChairCount; i++ {
		game.gameData.CurChairID++
		// 进行取模，防止超出
		game.gameData.CurChairID = game.gameData.CurChairID % game.gameData.ChairCount
		if game.IsPlayingChairId(game.gameData.CurChairID) {
			break
		}
	}
	// 4. 推送游戏状态
	game.gameData.GameStatus = PourScore
	pushMsg = map[string]any{
		"type": proto.GameStatusPush,
		"data": map[string]any{
			"gameStatus": game.gameData.GameStatus,
			"tick":       30,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 5. 操作推送
	pushMsg = map[string]any{
		"type": proto.GameTurnPush,
		"data": map[string]any{
			"curChairID": game.gameData.CurChairID,
			"curScore":   game.gameData.CurScore,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
}

// 结算
func (game *GameFrame) result(session *remote.Session, users []string) {
	// 推送状态
	game.gameData.GameStatus = Result
	pushMsg := map[string]any{
		"type": proto.GameStatusPush,
		"data": map[string]any{
			"gameStatus": game.gameData.GameStatus,
			"tick":       0,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	if game.gameResult == nil {
		game.gameResult = new(GameResult)
	}
	game.gameResult.Winners = game.gameData.Winner
	game.gameResult.HandCards = game.gameData.HandCards
	game.gameResult.CurScores = game.gameData.CurScores
	game.gameResult.Losers = game.gameData.Loser
	winScores := make([]int, game.gameData.ChairCount)
	for i := range winScores {
		if game.gameData.PourScores[i] != nil {
			scores := 0
			for _, sc := range game.gameData.PourScores[i] {
				scores += sc
			}
			winScores[i] = -scores
			for j := range game.gameData.Winner {
				winScores[j] += scores / len(game.gameData.Winner)
			}
		}
	}
	game.gameResult.WinScores = winScores
	pushMsg = map[string]any{
		"type": proto.GameResultPush,
		"data": map[string]any{
			"result": game.gameResult,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 重置游戏，开启下一把
	game.resetGame(session, users)
	game.gameEnd(session)
}

func (game *GameFrame) resetGame(session *remote.Session, users []string) {
	// 1. 重新初始化游戏
	game.gameData = initGameData(game.gameRule)
	// 2. 推送游戏状态
	game.gameData.GameStatus = GameStatus(None)
	pushMsg := map[string]any{
		"type": proto.GameStatusPush,
		"data": map[string]any{
			"gameStatus": game.gameData.GameStatus,
			"tick":       0,
		},
		"pushRouter": "GameMessagePush",
	}
	session.Push(users, pushMsg, "ServerMessagePush")
	// 3. 重置房间
	game.r.EndGame(session)
}

func (game *GameFrame) gameEnd(session *remote.Session) {
	// 赢家当庄家
	for i := range game.gameData.ChairCount {
		if game.gameResult.WinScores[i] > 0 {
			game.gameData.BankerChairID = i
			game.gameData.CurChairID = game.gameData.BankerChairID
		}
	}
	time.AfterFunc(5*time.Second, func() {
		for _, user := range game.r.GetUsers() {
			game.r.UserReady(user.UserInfo.Uid, session)
		}
	})
}

func (game *GameFrame) onGameAbandon(user *proto.RoomUserInfo, session *remote.Session) {
	if !game.IsPlayingChairId(user.ChairId) {
		return
	} else if ut.Contains(game.gameData.Loser, user.ChairId) {
		return
	}
	for i := range game.gameData.ChairCount {
		if game.IsPlayingChairId(i) && i != user.ChairId {
			game.gameData.Winner = append(game.gameData.Winner, i)
		}
	}
	game.gameData.Loser = append(game.gameData.Loser, user.ChairId)
	game.gameData.UserStatusArray[user.ChairId] = Abandon
	pushMsg := map[string]any{
		"type": proto.GameAbandonPush,
		"data": map[string]any{
			"chairID":    user.ChairId,
			"userStatus": game.gameData.UserStatusArray[user.ChairId],
		},
		"pushRouter": "GameMessagePush",
	}
	allUsers := game.getAllUsers()
	session.Push(allUsers, pushMsg, "ServerMessagePush")
	time.AfterFunc(time.Second, func() {
		game.endPourScore(allUsers, session)
	})
}
